﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using ProLinq.Toolkit;

namespace ProLinq.Interception
{
	/// <summary>
	///     Provider that intercepts query execution
	/// </summary>
	internal class InterceptionQueryProvider : QueryProvider
	{
		#region Fields

		internal IQueryable Source { get; private set; }
		internal List<InterceptionAction> Actions { get; private set; }

		#endregion

		#region Events

		#endregion

		#region Constructors

		public InterceptionQueryProvider(IQueryable source)
		{
			Source = source;
			Actions = new List<InterceptionAction>();
		}

		#endregion

		#region Methods

		protected Object ExecuteInternal(Expression expression)
		{
			if (expression.Type.IsEnumerable())
			{
				return Source.Provider.CreateQueryOfT(expression);
			}
			else
			{
				return Source.Provider.ExecuteOfT(expression);
			}
		}

		protected Object ExecuteAction(Int32 actionIndex, Expression expression)
		{
			if (actionIndex >= 0 && actionIndex < Actions.Count)
			{
				return Actions[actionIndex](new InterceptedQuery(expression, expr => ExecuteAction(actionIndex - 1, expr)));
			}
			return ExecuteInternal(expression);
		}

		#endregion

		#region Overrides

		public override IQueryable<TElement> CreateQuery<TElement>(Expression expression)
		{
			return new InterceptionQuery<TElement>(this, expression);
		}

		public override object Execute(Expression expression)
		{
			Expression modifiedExpression = new InterceptionPreProcessor {Source = Source}.Visit(expression);

			return ExecuteAction(Actions.Count - 1, modifiedExpression);
		}

		#endregion
	}
}